package com.feng.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feng.constants.SystemConstants;
import com.feng.domain.ResponseResult;
import com.feng.domain.entity.RoleMenu;
import com.feng.domain.vo.*;
import com.feng.enums.AppHttpCodeEnum;
import com.feng.mapper.MenuMapper;
import com.feng.domain.entity.Menu;
import com.feng.service.MenuService;
import com.feng.service.RoleMenuService;
import com.feng.utils.BeanCopyUtil;
import com.feng.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 菜单权限表(Menu)表服务实现类
 *
 * @author makejava
 * @since 2023-04-19 23:49:29
 */
@Service()
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Autowired
    private RoleMenuService roleMenuService;

    @Override
    public List<String> selectMenuByUserId(Long id) {
        if(SecurityUtils.isAdmin()){
            LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
            wrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
            List<Menu> list = list(wrapper);
            List<String> perms = list.stream()
                    .parallel()
                    .map(Menu::getPerms)
                    .collect(Collectors.toList());
            return perms;
        }
        List<String> perms = getBaseMapper().selectMenuByUserId(id);
        return perms;
    }

    @Override
    public List<MenuVo> selectRouterMenuTreeByUserId(Long userId) {
        List<Menu> menus = null;
        MenuMapper baseMapper = getBaseMapper();
        if (SecurityUtils.isAdmin()){
            menus = baseMapper.selectRouterMenuTreeByAdmin();
        }else {
            menus = baseMapper.selectRouterMenuTreeByUserId(userId);
        }
        List<MenuVo> menuVos = BeanCopyUtil.articleListVo(menus, MenuVo.class);

        List<MenuVo> menuVoList = buildMenuTree(menuVos, 0L);
        return menuVoList;
    }

    @Override
    public ResponseResult listAll(String status, String menuName) {
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.hasText(status), Menu::getStatus, status);
        wrapper.like(StringUtils.hasText(menuName), Menu::getMenuName, menuName);
        wrapper.orderByAsc(Menu::getParentId, Menu::getOrderNum);
        List<Menu> menus = list(wrapper);
        List<AdminMenuVo> adminMenuVos = BeanCopyUtil.articleListVo(menus, AdminMenuVo.class);
        return ResponseResult.okResult(adminMenuVos);
    }

    @Override
    public ResponseResult getMenu(Long id) {
        Menu menu = getById(id);
        AdminGetMenuVo adminGetMenuVo = BeanCopyUtil.articleVo(menu, AdminGetMenuVo.class);
        return ResponseResult.okResult(adminGetMenuVo);
    }

    @Override
    public ResponseResult updateMenu(Menu menu) {
        if (menu.getId().equals(menu.getParentId())){
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(),"修改菜单'文章管理'失败,上级菜单不能选择自己");
        }
        updateById(menu);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult deleteMenu(Long menuId) {
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Menu::getParentId, menuId);
        List<Menu> menus = list(wrapper);
        if(!menus.isEmpty()){
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(),"存在子菜单不允许删除");
        }
        getBaseMapper().deleteById(menuId);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult treeSelect() {
        List<Menu> menu = list();
        List<MenuTreeVo> menuTreeVos = BeanCopyUtil.articleListVo(menu, MenuTreeVo.class);
       for(int i = 0; i < menuTreeVos.size(); i++){
           menuTreeVos.get(i).setLabel(menu.get(i).getMenuName());
       }
        List<MenuTreeVo>  vos = adminMenuTree(menuTreeVos, 0L);
        return ResponseResult.okResult(vos);
    }

    @Override
    public ResponseResult roleTreeSelect(Long id) {
        List<Menu> menu = list();
        List<MenuTreeVo> menuTreeVos = BeanCopyUtil.articleListVo(menu, MenuTreeVo.class);
        for(int i = 0; i < menuTreeVos.size(); i++){
            menuTreeVos.get(i).setLabel(menu.get(i).getMenuName());
        }
        List<MenuTreeVo>  vos = adminMenuTree(menuTreeVos, 0L);
        LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleMenu::getRoleId, id);
        List<RoleMenu> roleMenus = roleMenuService.list(wrapper);
        List<Long> roleMenuIds = roleMenus.stream().parallel()
                .map(roleMenu -> roleMenu.getMenuId())
                .collect(Collectors.toList());
        return ResponseResult.okResult(new AdminRoleVo(vos, roleMenuIds));
    }

    private List<MenuTreeVo> adminMenuTree(List<MenuTreeVo> menuTreeVos, long parentId) {
        return menuTreeVos.stream().parallel()
                .filter(menuTreeVo -> menuTreeVo.getParentId().equals(parentId))
                .map(menuTreeVo -> {
                    return menuTreeVo.setChildren(getTreeChildren(menuTreeVos, menuTreeVo.getId()));
                })
                .collect(Collectors.toList());
    }

    private List<MenuTreeVo> getTreeChildren(List<MenuTreeVo> menuTreeVos, Long parentId) {
        return menuTreeVos.stream().parallel()
                .filter(menuTreeVo -> menuTreeVo.getParentId().equals(parentId))
                .map(menuTreeVo -> menuTreeVo.setChildren(getTreeChildren(menuTreeVos, menuTreeVo.getId())))
                .collect(Collectors.toList());
    }


    private List<MenuVo> buildMenuTree(List<MenuVo> menuVos, long userId) {
        return menuVos.stream().parallel()
                .filter(menuVo -> menuVo.getParentId().equals(userId))
                .map(menuVo -> menuVo.setChildren(getChildren(menuVos, menuVo.getId())))
                .collect(Collectors.toList());
    }

    private List<MenuVo> getChildren(List<MenuVo> menuVos, Long id) {
        return menuVos.stream().parallel()
                .filter(menuVo -> menuVo.getParentId().equals(id))
                .map(menuVo -> menuVo.setChildren(getChildren(menuVos, menuVo.getId())))
                .collect(Collectors.toList());
    }
}

